#!/usr/bin/env node

/*
 * 此脚本基于nvm实现不同项目使用不同的node版本
 *
 * 0. 安装nvm,使用nvm安装对应node版本；本文件放置于<project_root>/bin/inode.js
 *
 * 1. 项目目录下创建.node-version文件，内容为node版本号，如
 *    14.17.0: 明确的版本号,找不到时再以主版本号匹配
 *    16: 最新的主版本号为16的node
 *
 * 2. package.json中添加脚本"postinstall": "node bin/inodoe.js",执行安装命令(yarn install/npm install)后会自动运行
 *    运行后会创建
 *        node_modules/.node 相关node版本目录的软链接
 *        node_modules/.bin/node.exe 软链接
 *        node_modules/.bin/iyarn.cmd 脚本
 *
 * 3. [可选]使用 iyarn.cmd 运行脚本, eg: iyarn start
 *    有些前端项目使用脚本包了多层使用spawn运行的node进程就需要使用iyarn来运行脚本
 *    如果是直接运行的js或ts文件或node_modules/.bin下的脚本就不需要使用iyarn了
 *    WebStorm设置: Settings > Languages & Frameworks > Node.js > Package manager 选择(node_modules/.bin/iyarn.cmd)
 *    VSCode(v1.19.1): 打开package.json点击scripts下的脚本直接运行
 *
 *    另外,在webstorm的终端可以直接使用iyarn命令,因为webstorm会自动把node_modules/.bin目录添加到PATH环境变量中
 *    而VSCode需要手动把node_modules/.bin目录添加到PATH环境变量中或者通过".\node_modules\.bin\iyarn start"运行
 */

const fs = require("fs");
const path = require("path");

class VersionInfo {
    constructor(version) {
        this.version = version;
        const [v0, v1, v2] = version.split('.');
        this.major = v0;
        this.minor = v1 ?? "*";
        this.patch = v2 ?? "*";
    }

    equals(other) {
        return this.major === other.major && this.minor === other.minor && this.patch === other.patch;
    }
}

function findSpecNodeVersion(specVersion) {
    const nvmDir = process.env.NVM_HOME;
    if (!fs.existsSync(nvmDir)) return;
    const stats = fs.statSync(nvmDir);
    if (!stats.isDirectory()) return;

    const versions = [];
    const nodeDirs = fs.readdirSync(nvmDir, {withFileTypes: true});
    for (const nodeDir of nodeDirs) {
        if (nodeDir.isDirectory() && nodeDir.name.startsWith("v")) {
            const nodeVersion = nodeDir.name;
            const nodePaths = [
                path.resolve(nvmDir, nodeVersion, "node.exe"),
                path.resolve(nvmDir, nodeVersion, "node64.exe"),
            ];
            for (const nodePath of nodePaths) {
                if (fs.existsSync(nodePath)) {
                    const stats = fs.statSync(nodePath);
                    if (stats.isFile()) {
                        nodeBin = nodePath;
                        versions.push({version: new VersionInfo(nodeVersion.substring(1)), bin: nodePath});
                        break;
                    }
                }
            }
        }
    }

    const targetVersion = new VersionInfo(specVersion);
    // console.log(targetVersion);
    // console.log(versions);
    for (let i = versions.length - 1; i >= 0; i--) {
        const it = versions[i];
        // 查找完全匹配的版本
        if (it.version.equals(targetVersion)) {
            return it.bin;
        }
    }

    // 没找到
    for (let i = versions.length - 1; i >= 0; i--) {
        const it = versions[i];
        // 查找主版本号匹配的版本
        if (it.version.major === targetVersion.major) {
            return it.bin;
        }
    }

    return null;
}

function createNodeLink(nodeBin) {
    const nodeLinkPath = path.resolve(__dirname, "../node_modules/.bin/node.exe");
    if (fs.existsSync(nodeLinkPath)) {
        fs.unlinkSync(nodeLinkPath);
    }
    fs.symlinkSync(nodeBin, nodeLinkPath);
    console.log(nodeBin, "=>", nodeLinkPath);
}

function createNodeDirLink(nodeBin) {
    const linkDir = path.resolve(__dirname, "../node_modules/.node");
    if (fs.existsSync(linkDir)) {
        fs.unlinkSync(linkDir);
    }
    // 创建目录软链接
    const nodeDir = path.dirname(nodeBin);
    fs.symlinkSync(nodeDir, linkDir);
    console.log(nodeDir, "=>", linkDir);
    if (path.basename(nodeBin) === "node64.exe") {
        // 有些版本的node执行文件名是node64.exe，需要创建node.cmd文件
        const nodeCmdFile = path.resolve(nodeDir, "node.cmd");
        fs.writeFileSync(nodeCmdFile, `@echo off\r\n"${nodeBin}" %*`)
    }

    fs.writeFileSync(path.resolve(__dirname, "../node_modules/.bin/iyarn.cmd"), `@echo off
setlocal
set PATH=${linkDir};%PATH%
yarn %*
endlocal
`);
}

// __main__

const versionFile = path.resolve(__dirname, "../.node-version");
let nodeBin = "node";
if (fs.existsSync(versionFile)) {
    const specVersion = fs.readFileSync(versionFile).toString().trim();
    if (specVersion) {
        console.log("spec node version:", specVersion)
        const bin = findSpecNodeVersion(specVersion);
        if (bin) {
            nodeBin = bin;
        }
    }

}

console.log("found node path:", nodeBin);
if (nodeBin !== "node" && process.argv.length === 2) {
    // 创建软链接
    createNodeLink(nodeBin);
    createNodeDirLink(nodeBin);
}
